// Copyright 2019 Fuzhou Rockchip Electronics Co., Ltd. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "mjpeg_video_source.hh"
#include "utils.h"

namespace easymedia {

MJPEGVideoSource *MJPEGVideoSource::createNew(UsageEnvironment &env,
                                              FramedSource *source) {
  return new MJPEGVideoSource(env, source);
}

MJPEGVideoSource::MJPEGVideoSource(UsageEnvironment &env, FramedSource *source)
    : JPEGVideoSource(env), m_inputSource(source), m_width(0), m_height(0),
      m_qTable0Init(false), m_qTable1Init(false) {
  memset(&m_qTable, 0, sizeof(m_qTable));
}
MJPEGVideoSource::~MJPEGVideoSource() { Medium::close(m_inputSource); }
void MJPEGVideoSource::doGetNextFrame() {
  if (m_inputSource) {
    m_inputSource->getNextFrame(fTo, fMaxSize, afterGettingFrame, this,
                                FramedSource::handleClosure, this);
  }
}

void MJPEGVideoSource::doStopGettingFrames() {
  FramedSource::doStopGettingFrames();
  if (m_inputSource) {
    m_inputSource->stopGettingFrames();
  }
}
void MJPEGVideoSource::afterGettingFrame(void *clientData, unsigned frameSize,
                                         unsigned numTruncatedBytes,
                                         struct timeval presentationTime,
                                         unsigned durationInMicroseconds) {
  MJPEGVideoSource *source = (MJPEGVideoSource *)clientData;
  source->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime,
                            durationInMicroseconds);
}
void MJPEGVideoSource::afterGettingFrame(unsigned frameSize,
                                         unsigned numTruncatedBytes,
                                         struct timeval presentationTime,
                                         unsigned durationInMicroseconds) {
  int headerSize = 0;
  bool headerOk = false;
  fFrameSize = 0;

  for (unsigned int i = 0; i < frameSize; ++i) {
    // SOF
    if ((i + 8) < frameSize && fTo[i] == 0xFF && fTo[i + 1] == 0xC0) {
      m_height = (fTo[i + 5] << 5) | (fTo[i + 6] >> 3);
      m_width = (fTo[i + 7] << 5) | (fTo[i + 8] >> 3);
    }
    // DQT
    if ((i + 5 + 64) < frameSize && fTo[i] == 0xFF && fTo[i + 1] == 0xDB) {
      if (fTo[i + 4] == 0) {
        memcpy(m_qTable, fTo + i + 5, 64);
        m_qTable0Init = true;
      } else if (fTo[i + 4] == 1) {
        memcpy(m_qTable + 64, fTo + i + 5, 64);
        m_qTable1Init = true;
      }
    }
    // End of header
    if ((i + 1) < frameSize && fTo[i] == 0x3F && fTo[i + 1] == 0x00) {
      headerOk = true;
      headerSize = i + 2;
      break;
    }
  }

  if (headerOk) {
    fFrameSize = frameSize - headerSize;
    memmove(fTo, fTo + headerSize, fFrameSize);
  }

  fNumTruncatedBytes = numTruncatedBytes;
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}

u_int8_t const *MJPEGVideoSource::quantizationTables(u_int8_t &precision,
                                                     u_int16_t &length) {
  length = 0;
  precision = 0;
  if (m_qTable0Init && m_qTable1Init) {
    precision = 8;
    length = sizeof(m_qTable);
  }
  return m_qTable;
}

unsigned char MJPEGVideoSource::type() { return 1; };
unsigned char MJPEGVideoSource::qFactor() { return 128; };
unsigned char MJPEGVideoSource::width() { return m_width; };
unsigned char MJPEGVideoSource::height() { return m_height; };

} // namespace easymedia
